package csvchk

import (
	"bytes"
	"io"
	"strconv"
	"strings"
	"testing"
)

func Test_Check(t *testing.T) {

	csvstruct := "int*;varchar(10)"

	var r io.Reader
	w := new(bytes.Buffer)
	we := new(bytes.Buffer)

	// test (tab)
	r = strings.NewReader("1\tV1\r\n2\tV2\r\n3\tV3\r\n4\t\r\n")
	Check(csvstruct, '\t', false, false, r, w, we)
	equal(t, "1\tV1\n2\tV2\n3\tV3\n4\t\n", w.String(), "test (tab)")
	equal(t, "", we.String(), "test (tab)")
	w.Reset()
	we.Reset()

	// test (; & \r\n|\n)
	r = strings.NewReader("1;V1\n2;V2\r\n3;V3\r\n4;")
	Check(csvstruct, ';', false, false, r, w, we)
	equal(t, "1;V1\n2;V2\n3;V3\n4;\n", w.String(), "test (; & \r\n|\n)")
	equal(t, "", we.String(), "test (; & \r\n|\n)")
	w.Reset()
	we.Reset()

	// test (empty)
	r = strings.NewReader("")
	Check(csvstruct, ';', false, false, r, w, we)
	equal(t, "", w.String(), "test (empty)")
	equal(t, "", we.String(), "test (empty)")
	w.Reset()
	we.Reset()

	// test (header)
	r = strings.NewReader("KEY;VALUE\r\n2;V2\r\n3;V3\r\n4;\r\n")
	Check(csvstruct, ';', true, false, r, w, we)
	equal(t, "KEY;VALUE\n2;V2\n3;V3\n4;\n", w.String(), "test (skip 1)")
	equal(t, "", we.String(), "test (skip 1)")
	w.Reset()
	we.Reset()

	// test (empty, header)
	r = strings.NewReader("")
	Check(csvstruct, ';', true, false, r, w, we)
	equal(t, "", w.String(), "test (empty, skip 1)")
	equal(t, "", we.String(), "test (empty, skip 1)")
	w.Reset()
	we.Reset()

	// test (header only)
	r = strings.NewReader("KEY;VALUE")
	Check(csvstruct, ';', true, false, r, w, we)
	equal(t, "KEY;VALUE\n", w.String(), "test (skip 1, head only)")
	equal(t, "", we.String(), "test (skip 1, head only)")
	w.Reset()
	we.Reset()

	// test (trim)
	r = strings.NewReader(" 1, V1\r\n2,V2 \r\n 3 , V3 \r\n4,\r\n")
	Check(csvstruct, ',', false, true, r, w, we)
	equal(t, "1,V1\n2,V2\n3,V3\n4,\n", w.String(), "test (trim)")
	equal(t, "", we.String(), "test (trim)")
	w.Reset()
	we.Reset()

	// test (frmt)
	r = strings.NewReader("1,V1\n2,V2,\n3,V3\n4")
	Check(csvstruct, ',', false, true, r, w, we)
	equal(t, "1,V1\n3,V3\n", w.String(), "test (frmt)")
	equal(t, "-1\t\tFRMT\t\trecord on line 2: wrong number of fields\t2\n", we.String(), "test (frmt)")
	w.Reset()
	we.Reset()

	// test (type)
	r = strings.NewReader("1,V1\n2a,V2")
	Check(csvstruct, ',', false, true, r, w, we)
	equal(t, "1,V1\n", w.String(), "test (type)")
	equal(t, "0\tint\tTYPE\t2a\t2a¦V2\t1\n", we.String(), "test (type)")
	w.Reset()
	we.Reset()

}

func Test_RecordForPrint(t *testing.T) {
	tst := []string{"123", "ABC CD"}
	exp := "123¦ABC·CD"
	act := recordForPrint(tst)
	if exp != act {
		t.Errorf("'%v' <> '%v'\n", exp, act)
	}
}

func Test_StringForPrint(t *testing.T) {
	tst := "Text;Space: ;Tab:\t;LfCr:\n\r;Slash:\x5C;VTab:\x0B"
	exp := "Text;Space:·;Tab:\\t;LfCr:\\n\\r;Slash:\\\\;VTab:\\u000B"
	act := stringForPrint(tst)
	if exp != act {
		t.Errorf("'%v' <> '%v'\n", exp, act)
	}
}

func Benchmark_Check_base(b *testing.B) {
	csvstruct := "int*;varchar(10)"
	var tst string
	var exp string
	for l := 0; l < 1000; l++ {
		tst += strconv.Itoa(l) + "," + "text\n"
		exp += strconv.Itoa(l) + "," + "text\n"
	}
	r := strings.NewReader(tst)
	w := new(bytes.Buffer)
	we := new(bytes.Buffer)
	for i := 0; i < b.N; i++ {
		r.Reset(tst)
		Check(csvstruct, ',', false, false, r, w, we)
		if w.String() != exp {
			b.Fatalf("[%v] <> [%v]", w.String(), exp)
		}
		w.Reset()
		w.Truncate(0)
		if we.String() != "" {
			b.Fatalf("we = [%v]", we.String())
		}
		we.Reset()
		we.Truncate(0)
	}
}

func Benchmark_Check_trim(b *testing.B) {
	csvstruct := "int*;varchar(10)"
	var tst string
	var exp string
	for l := 0; l < 1000; l++ {
		tst += strconv.Itoa(l) + "," + " text \n"
		exp += strconv.Itoa(l) + "," + "text\n"
	}
	r := strings.NewReader(tst)
	w := new(bytes.Buffer)
	we := new(bytes.Buffer)
	for i := 0; i < b.N; i++ {
		r.Reset(tst)
		Check(csvstruct, ',', false, true, r, w, we)
		if w.String() != exp {
			b.Fatalf("[%v] <> [%v]", w.String(), exp)
		}
		w.Reset()
		w.Truncate(0)
		if we.String() != "" {
			b.Fatalf("we = [%v]", we.String())
		}
		we.Reset()
		we.Truncate(0)
	}
}

func Benchmark_Check_frmt(b *testing.B) {
	csvstruct := "int*;varchar(10)"
	var tst string
	var exp string
	for l := 0; l < 1000; l++ {
		tst += strconv.Itoa(l) + "," + "text"
		if l%2 == 1 {
			tst += ",\n"
		} else {
			tst += "\n"
			exp += strconv.Itoa(l) + "," + "text\n"
		}
	}
	r := strings.NewReader(tst)
	w := new(bytes.Buffer)
	we := new(bytes.Buffer)
	for i := 0; i < b.N; i++ {
		r.Reset(tst)
		Check(csvstruct, ',', false, false, r, w, we)
		if w.String() != exp {
			b.Fatalf("[%v] <> [%v]", w.String(), exp)
		}
		w.Reset()
		w.Truncate(0)
		if we.String() != "-1\t\tFRMT\t\trecord on line 2: wrong number of fields\t500\n" {
			b.Fatalf("we = [%v]", we.String())
		}
		we.Reset()
		we.Truncate(0)
	}
}
